/*
 * MockRMTransactionsFacade.java   ver    9 Nov 2009
 * 
 * Copyright (c) 2009 mentalsmash.org
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of the Apache License does not indicate that this project is
 * affiliated with the Apache Software Foundation.
 */
package org.mentalsmash.tazio.transactions.wrm;

import java.io.Serializable;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicInteger;

import org.mentalsmash.tazio.transactions.control.TazioTransaction;
import org.mentalsmash.tazio.transactions.model.StatusResponse;
import org.mentalsmash.tazio.transactions.model.TransactionalObject;
import org.mentalsmash.tazio.transactions.model.identifiers.OID;
import org.mentalsmash.tazio.transactions.model.identifiers.TID;
import org.mentalsmash.tazio.transactions.model.impl.TxObjectWrapper;
import org.mentalsmash.tazio.transactions.model.impl.TxObjectWrapperFactory;
import org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade;
import org.mentalsmash.tazio.transactions.user.Share;
import org.mentalsmash.tazio.transactions.user.TransactionalInterfaceException;
import org.mentalsmash.tazio.transactions.user.TransactionalSystemRuntimeException;
import org.mentalsmash.tazio.transactions.user.annotations.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.inject.Inject;

/**
 * Mocks an {@link RMTransactionsFacade}, locally.
 * 
 * @author Andrea Reale
 * @version 0.0.1
 */
public class MockRMTransactionsFacade implements RMTransactionsFacade {
    private final static Logger log = LoggerFactory
	    .getLogger(MockRMTransactionsFacade.class);
    private final AtomicInteger seqNoCtr = new AtomicInteger(0);
    private final Share rootSet;
    private final TxObjectWrapperFactory factory;

    @Inject
    protected MockRMTransactionsFacade(final Share rs,
	    final TxObjectWrapperFactory factory) {
	this.rootSet = rs;
	this.factory = factory;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade#beginTransaction
     * ()
     */
    @Override
    public long beginTransaction() {
	return this.seqNoCtr.get();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade#commitTransaction
     * (org.mentalsmash.tazio.transactions.impl.TazioTransaction)
     */
    @Override
    public long commitTransaction(final TazioTransaction transaction) {
	return this.seqNoCtr.incrementAndGet();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade#createObject
     * (java.lang.Class)
     */
    @Override
    public <T extends Serializable> T createObject(final Class<T> txInterface)
	    throws TransactionalInterfaceException {
	if (!checkTypeValidity(txInterface)) {
	    throw new TransactionalInterfaceException();
	}
	return createProxiedInstance(txInterface);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade#getRootObject
     * ()
     */
    @Override
    public Share getShare() {
	// TODO that's not serious
	return this.rootSet;

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade#removeFromRootSet
     * (org.mentalsmash.tazio.transactions.identifiers.OID)
     */
    @Override
    public boolean removeFromRootSet(final OID objectId) {
	// TODO Auto-generated method stub
	return false;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.mentalsmash.tazio.transactions.rm.RMTransactionsFacade#whatAboutThisTx
     * (org.mentalsmash.tazio.transactions.identifiers.TID)
     */
    @Override
    public StatusResponse whatAboutThisTx(final TID tid) {
	return StatusResponse.COMMITTED;
    }

    // PASTE FROM TazioRootSet.java

    /**
     * Given a suitable interface type (i.e. one who correctly implements
     * {@link Transactional} semantics), creates an instance of that interface
     * using the provided implementation.
     * 
     * @param <E>
     * 
     * @param txIface
     *            the interface to create a proxy for
     * @return an instance proxied with transactional logic
     */
    private <E> E createProxiedInstance(final Class<E> txIface) {
	Serializable instance = null;
	final TxObjectWrapper wrapper;
	final Class<?>[] interfaces; // set of interfaces for the proxy to
	// implement
	// retrieve the annotation. Already checked whether present or not.
	final Class<?> implementation = txIface.getAnnotation(
		Transactional.class).implementation();

	try {
	    instance = (Serializable) implementation.newInstance();
	} catch (final InstantiationException e) {
	    log.error("Fatal error while instantiating proxy", e);
	    throw new TransactionalSystemRuntimeException(e);
	} catch (final IllegalAccessException e) {
	    log.error("Fatal error while instantiating proxy", e);
	    throw new TransactionalSystemRuntimeException(e);
	}

	wrapper = this.factory.createTxWrapper(instance);
	interfaces = new Class<?>[] { txIface, TransactionalObject.class };

	final E newProxyInstance = txIface.cast(Proxy.newProxyInstance(txIface
		.getClassLoader(), interfaces, wrapper));

	return newProxyInstance;
    }

    /**
     * Checks whether objectType is an interface correctly marked with the
     * {@link Transactional} interface, and if the provided implementation is
     * suitable for instantiation
     * 
     * @return true if it is
     */
    private boolean checkTypeValidity(final Class<?> txIface) {
	Class<?> implementationClass;

	if (!txIface.isInterface()) {
	    log.error("{} must be an interface", txIface.getSimpleName());
	    return false;
	}

	if (!txIface.isAnnotationPresent(Transactional.class)) {
	    log.error("{} is not correctly annotated with @Transactional",
		    txIface.getSimpleName());
	    return false;
	}

	implementationClass = txIface.getAnnotation(Transactional.class)
		.implementation();

	try {
	    implementationClass.getConstructor(); // gets the
	    // default
	    // constructor
	} catch (final NoSuchMethodException e) {
	    log.error("Unable to create proxy for type {}."
		    + " No default constructor available "
		    + "in its implementation", implementationClass
		    .getSimpleName(), e);
	    return false;
	} catch (final SecurityException e) {
	    log.error("Unable to create proxy for type {}."
		    + " Security exception", implementationClass
		    .getSimpleName(), e);
	    return false;
	}

	return true;
    }
    // END

}
